package dhk;

import robocode.HitRobotEvent;
import robocode.HitWallEvent;
import robocode.Robot;
import robocode.ScannedRobotEvent;
import java.awt.geom.Point2D;

/**
 * Robot designed to counter most sample robots.
 * 
 * @author Dean Kim
 * @version 1.1
 * 
 */
public class CounterBot extends Robot {

  /** Last known energy of the target robot. */
  private double targetRobotPastEnergy;
  /** Target scanned status. */
  private boolean targetDetected = false;
  /** Gun turn rate constant. */
  private static final double GUN_TURN_RATE = 270;
  /** Robot's last updated heading. */
  private double currentHeading;
  /** Robot's last updated XY position. */
  private Point2D currentPosition = new Point2D.Double();
  /** Future robot position. */
  private Point2D futurePosition = new Point2D.Double();
  /** Tactical position. */
  private Point2D tacticalPosition = new Point2D.Double();
  /** Tactical heading. */
  private double tacticalHeading = 0;
  /** Battlefield center position. */
  private Point2D battleFieldCenter = new Point2D.Double();
  /** Target's last updated XY position. */
  private Point2D targetPosition = new Point2D.Double();
  /** Distance robot maneuvers to dodge bullet (4 * standard robot width). */
  private static final double DODGE_LENGTH = 160;
  /** Margin of degree error for firing criteria. */
  private static final double FIRE_DEGREE_MARGIN = 10.0;
  /** Number of turns passed since target last tracked. */
  private int targetTrackTurns = 0;
  /** Tactical position reached status 8. */
  private boolean tacticalPositionReached = false;
  /** Buffer distance to wall. */
  private double BUFFER_DISTANCE_TO_WALL = 160;

  /**
   * 
   * @see robocode.Robot#run()
   */
  public void run() {

    this.currentHeading = this.getHeading();
    this.currentPosition.setLocation(this.getX(), this.getY());
    this.battleFieldCenter.setLocation(MovementUtility.computeBattleFieldCenterX(this
        .getBattleFieldWidth()), MovementUtility.computeBattleFieldCenterY(this
        .getBattleFieldHeight()));
    this.scan();

    while (true) {
      this.currentHeading = this.getHeading();
      this.currentPosition.setLocation(this.getX(), this.getY());
      if (this.targetTrackTurns++ > 1) {
        this.turnGunRight(this.GUN_TURN_RATE);
      }
      if (!this.tacticalPositionReached) {

        this.maneuverToTacticalPosition();
        this.currentPosition.setLocation(this.getX(), this.getY());
        if ((this.currentPosition.getX() > this.tacticalPosition.getX() - 20)
            && (this.currentPosition.getX() < this.tacticalPosition.getX() + 20)) {
          this.tacticalPositionReached = true;
        } // end if
      } // end if
    } // end while
  }

  /**
   * 
   * @param e Scanned Robot Event
   * @see robocode.Robot#onScannedRobot(robocode.ScannedRobotEvent)
   */
  public void onScannedRobot(ScannedRobotEvent e) {

    double targetBotTrueBearing = 0;
    double gunTurnDegree = 0;
    boolean detectTargetFire = ((e.getEnergy() - this.targetRobotPastEnergy) > 0.9);
    double targetTrueBearing = 0;

    this.targetDetected = true;
    this.targetTrackTurns = 0;
    this.currentHeading = this.getHeading();
    this.currentPosition.setLocation(this.getX(), this.getY());

    this.targetPosition = TrackingUtility.computeTargetPositFromBearingRange(this.currentPosition,
        MovementUtility.convertRelativeToTrueBearing(currentHeading, e.getBearing()), e
            .getDistance());
    targetTrueBearing = MovementUtility
        .convertRelativeToTrueBearing(currentHeading, e.getBearing());

    if (this.tacticalPositionReached) {
      if ((targetTrueBearing < (this.getGunHeading() + FIRE_DEGREE_MARGIN))
          && (targetTrueBearing > (this.getGunHeading() - FIRE_DEGREE_MARGIN))) {

        // this.turnGunRight(this.computeFutureTargetBearing(e.getBearing(), e.getDistance(), e
        // .getHeading(), e.getVelocity()));
        this.fire(3);
      } // end if

      targetBotTrueBearing = MovementUtility.convertRelativeToTrueBearing(this.getHeading(), e
          .getBearing());
      gunTurnDegree = MovementUtility.computeRotationToBearing(this.getGunHeading(),
          targetBotTrueBearing);
      this.turnGunRight(gunTurnDegree);

      if (detectTargetFire) {
        System.out.println("evading");
        evadeTarget(e.getBearing());
      } // end if (detectTargetFire)
    } // end if (this.tacticalPositionReached == true)

  }

  /**
   * 
   * @param e Hit Wall event
   * @see robocode.Robot#onHitWall(robocode.HitWallEvent)
   */
  public void onHitWall(HitWallEvent e) {

    if ((e.getBearing() > 90) || (e.getBearing() < -90)) {
      this.ahead(151);
    }
    else {
      this.back(151);
    }

  }

  /**
   * Compute future position of walls robot.
   * 
   * @param detectedBearing Detected bearing of target robot
   * @param detectedRange Detected range of target robot
   * @param targetHeading Detected heading of target robot
   * @param speed detected speed of target robot
   * @return Rotation angle that estimates future target bearing
   */
  protected double computeFutureTargetBearing(double detectedBearing, double detectedRange,
      double targetHeading, double speed) {

    double futureBearing = 0;
    if (((targetHeading > 89.9) && (targetHeading < 90.01))
        || ((targetHeading > 189.9) && (targetHeading < 190.01))
        || ((targetHeading > 269.9) && (targetHeading < 270.01))
        || ((targetHeading < 359.9) && (targetHeading < 0.01))) {
      futureBearing = (speed * .187) * (detectedRange * .028);

    } // end if
    return futureBearing;
  }

  /**
   * Evades by vertically moving forward or back while attempting to remain centered along the Y
   * axis.
   * 
   * @param targetBearing Target Bearing
   */
  protected void evadeTarget(double targetBearing) {
    if (this.currentPosition.getY() < this.battleFieldCenter.getY()) {
      this.maintainGunHeadingAlongTargetBearing(CounterBot.DODGE_LENGTH);
      this.ahead(CounterBot.DODGE_LENGTH);
    }
    else {
      this.maintainGunHeadingAlongTargetBearing(-DODGE_LENGTH);
      this.ahead(-(CounterBot.DODGE_LENGTH));
    } // end else

  }

  /**
   * Aids in maintaining gun heading along contact.
   * 
   * @param moveDistance Distance robot intends to move
   */
  protected void maintainGunHeadingAlongTargetBearing(double moveDistance) {

    double futureGunTrueBearingToTarget = 0;
    double turnGunBearing = 0;

    this.futurePosition.setLocation(this.currentPosition.getX(),
        (this.currentPosition.getY() + moveDistance));
    futureGunTrueBearingToTarget = TrackingUtility.computeFiringTrueBearing(this.futurePosition,
        this.targetPosition);
    turnGunBearing = MovementUtility.computeRotationToBearing(this.getGunHeading(),
        futureGunTrueBearingToTarget);
    this.turnGunRight(turnGunBearing);
  }

  /**
   * 
   * @param e Hit robot event
   * @see robocode.Robot#onHitRobot(robocode.HitRobotEvent)
   */
  public void onHitRobot(HitRobotEvent e) {

    this.turnRight(60);
    if ((e.getBearing() > 90) || (e.getBearing() < -90)) {
      this.ahead(150);
    }
    else {
      this.back(150);
    }
  }

  /**
   * Detect if probable that target fired by monitoring energy level.
   * 
   * @param scannedEnergy Scanned energy of enemy robot
   * @return True if firing is detected or false if firing not detected
   */
  protected boolean detectTargetFiring(double scannedEnergy) {

    boolean fired = false;

    if ((scannedEnergy - this.targetRobotPastEnergy) >= 0.5) {
      fired = true;
    }

    this.targetRobotPastEnergy = scannedEnergy;
    return fired;

  }

  /**
   * Move to the tactical position at the center of the furthest vertical wall.
   * 
   */
  protected void maneuverToTacticalPosition() {

    if (this.targetDetected) {
      if (this.targetPosition.getX() < this.battleFieldCenter.getX()) {

        this.tacticalPosition.setLocation(
            this.getBattleFieldWidth() - this.BUFFER_DISTANCE_TO_WALL, this.battleFieldCenter
                .getY());
      } // end if (this.targetPosition.getX() < this.battleFieldCenter.getX())
      else {
        this.tacticalPosition.setLocation(0 + this.BUFFER_DISTANCE_TO_WALL, this.battleFieldCenter
            .getY());
      } // end else

      MovementUtility.moveToPositionAndRotate(this, this.currentHeading, this.currentPosition,
          this.tacticalHeading, this.tacticalPosition);
    } // end if
  }

  /**
   * Evade target or bullet by moving vertically.
   * 
   */

  protected void evadeFireVertical() {

    if (this.currentPosition.getY() <= this.battleFieldCenter.getY()) {
      this.ahead(CounterBot.DODGE_LENGTH);
    }
    else {
      this.back(CounterBot.DODGE_LENGTH);
    }
  }

  /**
   * Evade target or bullet by moving horizontally.
   * 
   */
  protected void evadeFireHorizontal() {

    if (this.currentPosition.getX() <= this.battleFieldCenter.getX()) {
      this.ahead(CounterBot.DODGE_LENGTH);
    }
    else {
      this.back(CounterBot.DODGE_LENGTH);
    }
  }

}
